<?php
/**
 *
 * Setup:
 *
    edit the singleton() metod 
    and define the list of memcached servers in a 2-d array
    in the format
    array(
        array('192.168.0.1'=>'11211'),
        array('192.168.0.2'=>'11211'),
    );
 *
 *
 * Usage:
 *
<?php
//include the class name
include ('memcache.class.php');

//store the variable
Cache::set('key','abc');

//increment/decrement the integer value
Cache::increment('key');
Cache::decrement('key');

//fetch the value by it's key
echo Cache::get('key');


//delete the data
echo Cache::delete('key');

//Clear the cache memory on all servers
Cache::flush();

?>

Cache::replace() and Cache::add are implemented also.

More information can be obtained here:
http://www.danga.com/memcached/
http://www.php.net/memcache

*/

/**
 * The class makes it easier to work with memcached servers and provides hints in the IDE like Zend Studio
 * @author Grigori Kochanov http://www.grik.net/
 * @version 1
 *
 */
class CConnectOneServer {
//class Cache {
/**
 * Resources of the opend memcached connections
 * @var array [memcache objects]
 */
static $mc_servers = array();
/**
 * Quantity of servers used
 * @var int
 */
static $mc_servers_count;

static $instance;

/**
 * Singleton to call from all other functions
 */
static function singleton($listArrayServer = array()){
    //Write here where from to get the servers list from, like 
    // global $servers	
    //$servers = _Config::$memcache_servers;
	$servers = $listArrayServer;
	
    
    self::$instance || 
        //self::$instance = new Cache($servers);
		self::$instance = new CConnectOneServer($servers);
    return self::$instance;
}

/**
 * Accepts the 2-d array with details of memcached servers
 *
 * @param array $servers
 */
protected function __construct(array $servers){
//public function __construct(array $servers){
    if (!$servers){
        trigger_error('No memcache servers to connect',E_USER_WARNING);
    }
    for ($i = 0, $n = count($servers); $i < $n; ++$i){
        ( $con = memcache_pconnect(key($servers[$i]), current($servers[$i])) )&& 
            $this->mc_servers[] = $con;
    }
    $this->mc_servers_count = count($this->mc_servers);
    if (!$this->mc_servers_count){
        $this->mc_servers[0]=null;
    }
}
/**
 * Returns the resource for the memcache connection
 *
 * @param string $key
 * @return object memcache
 */
protected function getMemcacheLink($key){
    if ( $this->mc_servers_count <2 ){
        //no servers choice
        return $this->mc_servers[0];
    }
    return $this->mc_servers[(crc32($key) & 0x7fffffff)%$this->mc_servers_count];
}

/**
 * Clear the cache
 *
 * @return void
 */
static function flush() {
    $x = self::singleton()->mc_servers_count;
    for ($i = 0; $i < $x; ++$i){
        $a = self::singleton()->mc_servers[$i];
        self::singleton()->mc_servers[$i]->flush();
    }
}

/**
 * Returns the value stored in the memory by it's key
 *
 * @param string $key
 * @return mix
 */
static function get($key) {
    return self::singleton()->getMemcacheLink($key)->get($key);
}

/**
 * Store the value in the memcache memory (overwrite if key exists)
 *
 * @param string $key
 * @param mix $var
 * @param bool $compress
 * @param int $expire (seconds before item expires)
 * @return bool
 */
static function set($key, $var, $compress=0, $expire=0) {
    return self::singleton()->getMemcacheLink($key)->set($key, $var, $compress?MEMCACHE_COMPRESSED:null, $expire);
}
/**
 * Set the value in memcache if the value does not exist; returns FALSE if value exists
 *
 * @param sting $key
 * @param mix $var
 * @param bool $compress
 * @param int $expire
 * @return bool
 */
static function add($key, $var, $compress=0, $expire=0) {
    return self::singleton()->getMemcacheLink($key)->add($key, $var, $compress?MEMCACHE_COMPRESSED:null, $expire);
}

/**
 * Replace an existing value
 *
 * @param string $key
 * @param mix $var
 * @param bool $compress
 * @param int $expire
 * @return bool
 */
static function replace($key, $var, $compress=0, $expire=0) {
    return self::singleton()->getMemcacheLink($key)->replace($key, $var, $compress?MEMCACHE_COMPRESSED:null, $expire);
}
/**
 * Delete a record or set a timeout
 *
 * @param string $key
 * @param int $timeout
 * @return bool
 */
static function delete($key, $timeout=0) {
    return self::singleton()->getMemcacheLink($key)->delete($key, $timeout);
}
/**
 * Increment an existing integer value
 *
 * @param string $key
 * @param mix $value
 * @return bool
 */
static function increment($key, $value=1) {
    return self::singleton()->getMemcacheLink($key)->increment($key, $value);
}

/**
 * Decrement an existing value
 *
 * @param string $key
 * @param mix $value
 * @return bool
 */
static function decrement($key, $value=1) {
    return self::singleton()->getMemcacheLink($key)->decrement($key, $value);
}


//class end
}

class CConnectDB{
	public $i = 1;
	/**
	 * @var string handle của mysql 
	 */
	public $id = false;
	/**
	 * @var string Host của database
	 */
	public $host;
	/**
	 * @var string username của database
	 */
	public $username;
	/**
	 * @var string password của database
	 */
	public $password;
	/**
	 * @var string tên của database
	 */
	public $database;
	
	public $slave_id = false;
	/**
	 * @var string Host của database
	 */
	public $slave_host;
	/**
	 * @var string username của database
	 */
	public $slave_username;
	/**
	 * @var string password của database
	 */
	public $slave_password;
	public $slave_database;
	public $slave_connected = false;
	public $connected = false;

	/**
	 * Khởi tạo đối tượng CMySQLStore
	 *
	 * @param Địa chỉ $host
	 * @param Username của DB $username
	 * @param mật khẩu của DB $password
	 * @param tên của DB $database
	 */
	function __construct($host, $username, $password, $database, $slave_host = false, $slave_username = false, $slave_password = false, $slave_database = false)
	{
		$this->host = $host;
		$this->username = $username;
		$this->password = $password;
		$this->database = $database;
		$this->slave_host = $slave_host?$slave_host:$host;
		$this->slave_username = $slave_username?$slave_username:$username;
		$this->slave_password = ($slave_password!==false)?$slave_password:$password;
		$this->slave_database = $slave_database?$slave_database:$database;
	}
	/**
	 * Phương thức tạo kết nối đến DB
	 *
	 */
	function connect(){
		if(!$this->connected){
			$this->id = mysql_connect($this->host, $this->username, $this->password);
			mysql_select_db($this->database);
			$this->connected = true;			
		}
	}
	function slave_connect(){				
		$this->connect();
	}
	/**
	 * Phương thức đóng DB
	 *
	 */
	function close(){
		if($this->connected){
			mysql_close($this->id);
			$this->connected = false;
		}		
	}
	/**
	 * Phương thức lấy ra bản ghi đầu tiên của bảng table trong CSDL
	 *
	 * @param Tên bảng $table
	 * @param Điểu kiện lựa chọn các bản ghi $condition kiểu array
	 * @param Các tùy chọn lựa chọn các bản ghi trong bảng $options kiểu mảng
	 * @return Nội dung bản ghi đầu tiên trong bảng table
	 */
	function select($query = '')
	{		
		$this->slave_connect();		
		// khoanb		
		//echo $query."<br />{$this->i}<br />";		
		return $this->fetch($query, true);
	}
	/**
	 * Phương thức đếm số bản ghi thỏa mãn ĐK
	 *
	 * @param Tên bảng $table kiểu chuỗi
	 * @param Các điều kiện $conditions kiểu mảng
	 * @return Số bản ghi thỏa mãn điều kiện conditions
	 */
	function count($table, $conditions=false)
	{
		$this->slave_connect();
		$condition_string = $this->condition_string($conditions);
		$sql = '
			SELECT
				count(*) as total
			FROM
				`'.$table.'`
			'.($condition_string?'
			WHERE
				'.$this->condition_string($conditions):'').'
		';
		$row = $this->fetch('
			SELECT
				count(*) as total
			FROM
				`'.$table.'`
			'.($condition_string?'
			WHERE
				'.$this->condition_string($conditions):'').'
		', true);
		// Khoanb
		//echo "{$sql}<br /><strong>{$this->i}</strong><br />";
		//$this->i++;
		return $row['total'];
	}
	
	/**
	 * Xử lý toán học cho một trường
	 *  VD: max, min, ....
	 *
	 * @param string $math_fn
	 * @param string $fieldname
	 * @param string $table
	 * @param array $conditions
	 * @return mixed
	 */
	function math_fn($math_fn, $fieldname, $table, $conditions=false)
	{
		$this->slave_connect();
		$condition_string = $this->condition_string($conditions);
		$query = '
			SELECT '.$math_fn.
				'(`'.$fieldname.'`) as result
			FROM
				`'.$table.'`
			'.($condition_string?'
			WHERE
				'.$this->condition_string($conditions):'').'
		';
		$row = $this->fetch($query, true);
		return $row['result'];
	}
	
	/**
	 * Thực hiện một truy vấn đến DB
	 *
	 * @param Nội dung truy vấn $query_string thuộc kiểu chuỗi
	 * @return Kết quả của truy vấn
	 */
	function query($query_string, $slave = false)
	{
		if(DEBUG){
			CLog()->log('QUERY',$query_string);
		}
		try{
			if($this->id){
				$result = mysql_query($query_string,$this->id);
			}else{
				return false;
			}
		}
		catch(Exception $e){
			return false;
		}
		return $result;
	}
	
	function fetch($query_string = false, $slave = false)
	{
		try
		{
			if(!$query_string)
			{
				return mysql_fetch_assoc($this->result);
			}
			elseif($this->result = $this->query($query_string, $slave))
			{
				return mysql_fetch_assoc($this->result);
			}
		}
		catch(Exception $e)
		{
		}
	}
	/**
	 * Phương thức chèn bản ghi có giá trị values vào trong bảng
	 *
	 * @param tên bảng $table kiểu chuỗi
	 * @param Các giá trị của bản ghi  $values kiểu array
	 * @return Bảng sau khi đã chèn bản ghi
	 */
	function insert($table, $values)
	{
		$this->connect();
		$query='insert into';
		$query.=' `'.$table.'`(';
		$i=0;
		if(is_array($values))
		{
			foreach($values as $key=>$value)
			{
				if(($key===0) or is_numeric($key))
				{
					$key=$value;
				}
				if($key)
				{
					if($i<>0)
					{
						$query.=',';
					}
					$query.='`'.$key.'`';
					$i++;
				}
			}
			$query.=') values(';
			$i=0;
			foreach($values as $key=>$value)
			{
				if(is_numeric($key) or $key===0)
				{
					$value=Url::get($value);
				}
				if($i<>0)
				{
					$query.=',';
				}

				if($value==='NULL')
				{
					$query.='NULL';
				}
				else
				{
					$query.='\''.(is_array($value)?'':mysql_real_escape_string($value)).'\'';
				}
				$i++;
			}
			
			$query.=')';
			// Khoanb
			//echo $query."<br />{$this->i}<br />";
			//$this->i++;
//			file_put_contents(DEBUG_DIR.'log.txt', $query);
			if($this->query($query))
			{
				return mysql_insert_id($this->id);
			}
		}
	}
	function save($query = ''){
		$this->connect();
		if($this->query($query)){
			return true;
		}
	}
	/**
	 * Phương thức cập nhật các giá trị mới cho các bản ghi thỏa mãn điều kiện conditions
	 *
	 * @param tên bảng $table kiểu chuỗi
	 * @param Các giá trị mới cần cập nhật $values kiểu array
	 * @param điều kiện của các bản ghi được cập nhật $conditions
	 * @return Trả về giá trị True nếu các bản ghi được cập  nhật thành công.
	 */
	function update($table, $values, $conditions = array()){
		$this->connect();
		$query='update `'.$table.'` set ';
		$i=0;
		$sCondition = '';
		if($values){
			foreach($values as $key=>$value){				
				if($key===0 or is_numeric($key)){
					$key=$value;
					$value=request($value);
				}
				if($i<>0){
					$query.=',';
				}
				if($key){
					if($value==='NULL'){
						$query.='`'.$key.'`=NULL';
					}else{
						$query.='`'.$key.'`=\''.mysql_real_escape_string($value).'\'';
					}
					$i++;
				}
			}
			if(count($conditions) > 0){
				foreach($conditions as $k => $v){
					$sCondition .= " AND {$k} = '{$v}' ";
				}
				
				$query.=' where 1 '.$sCondition;
			}
			//khoanb
			//echo $query."<br />{$this->i}<br />";
			//$this->i++;
//			file_put_contents(DEBUG_DIR.'log.txt',$query);
			if($this->query($query))
			{
				return true;
			}
		}
	}
	/**
	 * Phương thức xóa các bản ghi thỏa mãn điều kiện conditions
	 *
	 * @param tên bảng $table kiểu 
	 * @param unknown_type $conditions
	 */
	function delete($table, $conditions)
	{
		$this->connect();
		$sql = 'DELETE FROM `'.$table.'` WHERE '.$this->condition_string($conditions);
		$this->query('DELETE FROM `'.$table.'` WHERE '.$this->condition_string($conditions));
		// Khoanb
		//echo "{$sql}<br />{$this->i}<br />";
		//$this->i++;
	}
	static function convert_conditions($operator, $factors)
	{
		switch ($operator) {
			case 'COLUMN':
				return ' `'.addslashes($factors[0]).'` ';
				break;
			case 'FULLTEXT':
				return 'MATCH ('.$factors[0].') AGAINST ('.$factors[1].' IN BOOLEAN MODE)';
				break;
			case 'LIKE':
				return '('.$factors[0].' LIKE "%'.substr($factors[1],2,strlen($factors[1])-4).'%")';
				break;
			case 'NLIKE':
				return '('.$factors[0].' NOT LIKE "%'.substr($factors[1],2,strlen($factors[1])-4).'%")';
				break;
			case 'BW':
				return '('.$factors[0].' LIKE "'.substr($factors[1],2,strlen($factors[1])-4).'%")';
				break;
			case 'EW':
				return '('.$factors[0].' LIKE "%'.substr($factors[1],2,strlen($factors[1])-4).'")';
				break;
			case 'INT':
				$factors[0] = intval($factors[0]);
				return ' "'.mysql_escape_string($factors[0]).'" ';
				break;
			case 'REAL':
				$factors[0] = floatval($factors[0]);
				return ' "'.mysql_escape_string($factors[0]).'" ';
				break;
			case 'STRING':
				return ' "'.mysql_escape_string($factors[0]).'" ';
				break;
			case 'ID':
				return '(`id` = '.$factors[0].')';
				break;
			case '!':
				return ' NOT '.$factors[0].' ';
				break;
			case '!=':
				return '('.$factors[0].' <> '.$factors[1].')';
				break;
			case 'NOT_NULL':
				return '('.$factors[0].' is not null)';
				break;
			case 'NULL':
				return '('.$factors[0].' is null)';
				break;
			case 'IN':
				$sub_factors = array();
				for($i = 1; $i< sizeof($factors); $i++)
				{
					$sub_factors[] = '( '.$factors[0].' = '.$factors[$i].' )';
				}
				return '( '.join(' OR ', $sub_factors).' )';
				break;
			default:
				return '( '.join(' '.$operator.' ', $factors).' )';
				break;
		}
	}	
	function select_all($query = ''){
		$items = array();
		if(!empty($query)){
			if($item = $this->select($query)){
				do{
					if(isset($item['id'])){
						$items[$item['id']] = $item;
					}else {
						$items[] = $item;
					}
				}
				while($item = $this->fetch());
			}
		}
		return $items;
	}

} // End class

?>